tree grammar ProgmaTreeParser;

options {
  tokenVocab = ProgmaParser;
  ASTLabelType = CommonTree;
  superClass = ProgmaBaseTreeParser;
}

@header {
package progma.lang.compiler;
}

program
@after {
  bg.end();
}
    : ^(PROGRAM module_import_stmt? fixity* {checkInitedClass();} def+)
    ;

// packages and imports

module_import_stmt
    : module_stmt
    | import_stmt
    ;

module_stmt
    : ^(MODULE VAR import_stmt?)
      {startClass($VAR.text);}
    ;

import_stmt
    : ^(IMPORT package_name+)
    ;

package_name
    : VAR
    | VARWILD
    ;

// fixity

fixity
    : ^(INFIX OPERATOR NUMBER)
    ;

// data definition

def
    : typedef
    | defvar
    | defun
    ;

typedef
    : classdef
    ;

classdef
    : ^(CLASS name=VAR metadata* fls+=VAR*)
      {newClass($name.text, $fls);}
    ;

defvar
    : ^(COLONEQUALS VAR metadata* expr)
      {newVariable($VAR.text);}
    ;

defun
@after {
  endFunction();
}
    : ^(DEFUN n=VAR metadata* ^(PARS ps+=VAR*) fun_body)
      {newFunction($n.text, $ps);}
    | ^(DEFUN OPERATOR metadata* ^(PARS ps+=VAR+) fun_body)
      {newFunction($OPERATOR.text, $ps);}
    ;

fun_body
    : statement*
    ;

// lambda

lambda
    : ^(LAMBDA ^(PARS VAR*) statement*)
    ;

// statements

statement
    : defvar
    | assignment
    | defun
    | expr
    | while_stmt
    | try_stmt
    | match_stmt
    ;

assignment
    : ^(EQUALS VAR metadata* expr)
    ;

while_stmt
    : ^(WHILE expr statement*)
    ;

try_stmt
    : ^(TRY statement* catch_stmt* finally_stmt?)
    ;

catch_stmt
    : ^(CATCH VAR lambda)
    ;

finally_stmt
    : ^(FINALLY statement*)
    ;

// pattern matching

match_stmt
    : ^(MATCH expr case_stmt+)
    ;

case_stmt
    : ^(CASE case_ctor statement*)
    ;

case_ctor
    : NUMBER
    | STRING
    | WILD
    | list_patt
    | ctor_patt
    ;

list_patt
    : ^(LIST_PATT VAR*)
    ;

ctor_patt
    : ^(CTOR_PATT VAR VAR?)
    ;

// expressions

expr
    : ^(OPERATOR expr expr)
    | function
    ;

function
    : ^(CALL literals expr*)
    | literals
    ;

literals
    : java_call
    | atom
    | VAR
    | table
    | list
    | lambda
    | if_expr
    | OPERATOR
    ;

java_call
    : ^(JCALL VAR pars_java_call+)
    | ^(JCALL STRING pars_java_call+)
    ;

pars_java_call
    : ^(JFUN VAR expr*)
    ;

if_expr
    : ^(IF expr ^(THEN statement*) statement*)
    ;

// literals and syntax-supported types

list
    : ^(LIST expr*)
    ;

table
    : ^(TABLE table_tuple*)
    ;

table_tuple
    : ^(KEYVAL expr expr)
    ;

atom
    : NUMBER
    | STRING
    | CHAR
    | BOOL
    | NULL
    ;

// metadata

metadata
    : ^(META VAR simple_var*)
    ;

simple_var
    : atom
    | VAR
    ;